﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Collections;
using System.Threading;
using SHack.Helper;
using System.Web;


namespace SHack
{
    class WebScan:BaseScan
    {
       
        public delegate void ScanFoundUrlHandler(string fountUrl,string retCode);
        public event ScanFoundUrlHandler ScanFoundUrlEvent;

        public delegate void ScaningHandler(string scanUrl);
        public event ScaningHandler ScaningEvent;

        private string[] _ltFile  ;
        private string[] _ltDir ;
        private string[] _ltMdb ;
        private string[] _ltScript;


        private int _curMdb;
        private int _curDir;
        private int _curFile;
        private int _curScript;
        private object _lockerMdb=new object();
        private object _lockerDir = new object();
        private object _lockerFile = new object();
        private object _lockerScript = new object();


        private string _filePath;
        private string _url;

        #region Pre
        public int FileCount
        {
            get { return _ltFile==null?0:_ltFile.Length; }
        }

        public int DirCount
        {
            get { return _ltDir.Length; }
        }

        public int MdbCount
        {
            get { return _ltMdb.Length; }
        }

        public int ScriptCount
        {
            get { return _ltScript.Length; }
        }

        public int TotalDataCount
        {
            get { return _ltFile == null ? 0 : _ltFile.Length + _ltDir.Length + _ltMdb.Length + _ltScript.Length; }
        }
        #endregion

        public bool Save()
        {
            if (_filePath == null || _ltFile.Length == 0)
                return false;

            Load(_filePath);

            try
            {
                StreamWriter sw = new StreamWriter(_filePath);
                foreach (string key in _ltFile)
                {
                    sw.WriteLine(key);
                }

                foreach (string key in _ltDir)
                {
                    sw.WriteLine(key);
                }

                foreach (string key in _ltMdb)
                {
                    sw.WriteLine(key);
                }

                foreach (string key in _ltScript)
                {
                    sw.WriteLine(key+".php");
                }
                sw.Close();

                return true;
            }
            catch
            {
                return false;
            }
        }

        public bool Load(string filePath)
        {
            if (File.Exists(filePath))
                _filePath = filePath;
            else
                return false;

            Hashtable htFile = new Hashtable();
            Hashtable htDir = new Hashtable();
            Hashtable htMdb = new Hashtable();
            Hashtable htScript = new Hashtable();

            List<string> ltFile = new List<string>();
            List<string> ltDir = new List<string>();
            List<string> ltMdb = new List<string>();
            List<string> ltScript = new List<string>();

            foreach (string rline in File.ReadAllLines(filePath,Encoding.UTF8))
            {
                try
                {
                    string r = rline.Trim();
                    if (r.IndexOf("/")!=0)
                      r = "/" + r;

                    if (r.Contains("."))
                    {
                        string rr = r.ToLower();

                        if (rr.Contains(".mdb"))
                        {
                            if (!htMdb.ContainsKey(r))
                            {
                                htMdb.Add(r, null);
                                ltMdb.Add(r);
                            }
                        }
                        else if (rr.Contains(".cgi") || rr.Contains(".html") || rr.Contains(".asa") ||  rr.Contains(".htm") || rr.Contains(".asp") || rr.Contains(".php") || rr.Contains(".aspx") || rr.Contains("jsp") || rr.Contains(".ac"))
                        {
                            int lastPoint = r.LastIndexOf(".");
                            string fileShort = r.Substring(0, lastPoint).TrimStart();
                            if (!htScript.ContainsKey(fileShort))
                            {
                                htScript.Add(fileShort, null);
                                ltScript.Add(fileShort);
                            }
                        }
                        else
                        {
                            if (!htFile.ContainsKey(r))
                            {
                                htFile.Add(r, null);
                                ltFile.Add(r);
                            }
                        }
                    }
                    else
                    {
                        if (!htDir.ContainsKey(r))
                        {
                            htDir.Add(r, null);
                            ltDir.Add(r);
                        }
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
            }

            _ltDir = ltDir.ToArray();
            _ltFile = ltFile.ToArray();
            _ltMdb = ltMdb.ToArray();
            _ltScript = ltScript.ToArray();

            return true;            
        }

        public bool Stop()
        {
            _isrun = false;
            return true;
        }

        public int Start(string url,int threadCount,int timeOut,bool sDir,bool sMdb,bool sFile,string[] sScriptExts)
        {
            _error = "";
            _curMdb =0;
            _curDir =0;
            _curFile = 0;
            _url = url;
            _timeOut = timeOut;
            _passCount = 0;
            _totalCount = 0;


            if (_ltFile.Length == 0 && _ltDir.Length == 0 && _ltMdb.Length == 0)
            {
                _error ="没有URL库数据，请先完善【URL.txt】地址库后，再运行本程序！";
                return -1;
            }
            try
            {

                _isrun = true;
                _lastTime = DateTime.Now;
                _totalCount = 0;

                int totalTargetType = 0;
                if (sDir)
                {
                    _totalCount = _ltDir.Length;
                    totalTargetType++;
                }
                if (sMdb)
                {
                    _totalCount += _ltMdb.Length;
                    totalTargetType++;
                }

                if (sFile)
                {
                    _totalCount += _ltFile.Length;
                    totalTargetType++;
                }

                if (sScriptExts.Length > 0)
                {
                    _totalCount += _ltScript.Length * sScriptExts.Length;
                    totalTargetType++;
                }

                int avgThreadCount = threadCount / totalTargetType;

                if (sDir)
                {
                    for (int i = 0; i < avgThreadCount; i++)
                    {
                        if (_ltDir.Length > 0)
                        {
                            new Thread(thScanDir).Start();
                        }
                    }
                }

                if (sMdb)
                {
                    for (int i = 0; i < avgThreadCount; i++)
                    {
                        if (_ltMdb.Length > 0)
                        {
                            new Thread(thScanMdb).Start();
                        }
                    }
                }


                if (sFile)
                {
                    for (int i = 0; i < avgThreadCount; i++)
                    {
                        if (_ltFile.Length > 0)
                        {
                            new Thread(thScanFile).Start();
                        }
                    }
                }


                if (sScriptExts.Length > 0)
                {
                    int fileThread = threadCount - avgThreadCount * (totalTargetType - 1);
                    for (int i = 0; i < fileThread; i++)
                    {
                        if (_ltFile.Length > 0)
                        {
                            new Thread(new ParameterizedThreadStart(thScanScript)).Start(sScriptExts);
                        }
                    }

                }

                return _totalCount;
            }
            catch(Exception ex)
            {
                _error = ex.Message;
                return -1;
            }
        }

        private void thScanDir()
        {
            string thisUrl;
            int retCode;
            HttpHelperLite http = new HttpHelperLite();
            http.TimeOut = _timeOut;
            while (_isrun && _curDir <_ltDir.Length)
            {
                try
                {
                    int index;
                    lock (_lockerDir)
                    {
                        index = _curDir++;
                    }
                    AddPass();

                    thisUrl = _url + HttpUtility.UrlPathEncode(_ltDir[index]);
                    retCode = http.Get(thisUrl);



                    if (ScaningEvent != null)
                        ScaningEvent(thisUrl);

                    if (retCode==200)
                    {
                        AddSucc();
                        if (ScanFoundUrlEvent != null)
                            ScanFoundUrlEvent(thisUrl, "200");
                    }
                }
                catch { }
            }

            CallStop();
        }

        private void thScanMdb()
        {
            string thisUrl;
            int retCode;
            HttpHelperLite http = new HttpHelperLite();
            http.TimeOut = _timeOut;
            while (_isrun && _curMdb<_ltMdb.Length)
            {
                try
                {
                    int index;
                    lock (_lockerDir)
                    {
                        index = _curMdb++;
                    }
                    AddPass();

                    thisUrl = _url + HttpUtility.UrlPathEncode(_ltMdb[index]);
                    retCode = http.Get(thisUrl);

                    if (ScaningEvent != null)
                        ScaningEvent(thisUrl);

                    if (retCode==200)
                    {
                        AddSucc();
                        if (ScanFoundUrlEvent != null)
                            ScanFoundUrlEvent(thisUrl, "200");
                    }
                }
                catch { }
            }

            CallStop();
        }

        private void thScanFile()
        {
            string thisUrl;
            int retCode;
            HttpHelperLite http = new HttpHelperLite();
            http.TimeOut = _timeOut;
            while (_isrun && _curFile < _ltFile.Length)
            {
                try
                {
                    int index;
                    lock (_lockerFile)
                    {
                        index = _curFile++;
                    }

                    AddPass();

                    thisUrl = _url + HttpUtility.UrlPathEncode(_ltFile[index]);
                    retCode = http.Get(thisUrl);

                    if (ScaningEvent != null)
                        ScaningEvent(thisUrl);

                    if (retCode==200)
                    {
                        AddSucc();
                        if (ScanFoundUrlEvent != null)
                            ScanFoundUrlEvent(thisUrl, "200");
                    }
                }
                catch { }
            }

            CallStop();
        }

        private void thScanScript(object obj)
        {
            string[] fileExts = (string[])obj;
            string thisUrl;
            int retCode;
            HttpHelperLite http = new HttpHelperLite();
            http.TimeOut = _timeOut;
            while (_isrun && _curScript < _ltScript.Length)
            {
                try
                {
                    int index;
                    lock (_lockerScript)
                    {
                        index = _curScript++;
                    }
                    foreach (string ext in fileExts)
                    {

                        AddPass();

                        thisUrl = _url + _ltScript[index] + "." + ext;
                        retCode = http.Get(thisUrl);


                        if (ScaningEvent != null)
                            ScaningEvent(thisUrl);

                        if (retCode==200)
                        {
                            AddSucc();
                            if (ScanFoundUrlEvent != null)
                                ScanFoundUrlEvent(thisUrl, "200");
                        }
                    }
                }
                catch { }
            }

            CallStop();
        }

    }
}
